iT邦幫忙

2023 iThome 鐵人賽

DAY 10
0
Modern Web

React 走出新手村 系列 第 10

React 走出新手村-useMemo & useCallback 小技巧

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20230910/20129020nQPyq7IPPd.jpg

挑刺

前一篇我們詳細解釋了 useMemouseCallback 的運作原理和邏輯,今天我們就來檢討一些使用不好的例子。

首先,我們都已經知道這兩個 hook 是來幫助我們處理深入比較傳入參數的新舊值,是否完全相等,那麼這樣的處理我們在 useEffect 的篇章也提及了怎麼實現。

從大O角度思考

那麼我們都有了基本的概念,會理解到每做一次的 deep compare 就會觸發多一層的歷遍,那有大O基礎的朋友應該就不難理解應該要如何避免。

簡單來說,就要避免像俄羅斯套娃那種層層堆疊的處理,既然每次的使用都是指數型耗能,那肯定要用在刀口上才行,那我們來看看一些使用的好與不好的範例吧!

useMemo:

  • 比較不好的:

    // bad
    // 數字相加
    const sum = useMemo(() => num1 + num2, [num1, num2])
    // 這個就是比較差的使用情況,都是可以直接運算出結果的就不需要在進一次
    // 改成
    const sum = num1 + num2;
    // 字串相加
    const fullstr = useMemo(() => `${str1} ${str2}`, [str1, str2])
    // 一樣的情況在字串上也都是一樣的
    // 改成
    const fullstr = `${str1} ${str2}`;
    
  • 比較好的:

    // good
    // 陣列型別
    const doubleNums = useMemo(() => nums.map((num) => num * 2), [nums])
    // 處理陣列,同樣也可以用在filter, reduce...其他的方法情況
    // 物件型別
    const playerInfo = useMemo(() => ({
        name
        hp,
        mp,
        carryweight: hp*1.5 + mp*2
    }), [name, hp, mp])
    // 陣列和物件都是by reference的,在處理上會顯得比較有意義一點
    // 此外,也要盡量避免相互層套
    const playerInfo2 = useMemo(() => ({
        name,
        hp,
        mp,
        carryweight: hp*1.5 + mp*2,
        doubleNums // 已經是useMemo處理過的東西
    }), [name, hp, mp, doubleNums])
    

useCallback

那我們接著來看看 useCallback 以上一篇為例。

  • 比較不好的

    import React, { useState, useCallback } from 'react';
    
    const ParentComponent = () => {
      const [count, setCount] = useState(0);
        // 比較不好的
      const increment = useCallback(() => {
            // 直接拿取count來修改
        setCount(count + 1);
            // dependencies array又必須拿count,那每次執行又會重建一次function
      }, [count]);
    
      return (
        <div>
          <ChildComponent onClick={increment} />
          <p>Count: {count}</p>
        </div>
      );
    };
    
    const ChildComponent = ({ onClick }) => {
      console.log('我被渲染啦');
      return <button onClick={onClick}>Increment</button>;
    };
    
    export default ParentComponent;
    
  • 比較好的

    import React, { useState, useCallback } from 'react';
    
    const ParentComponent = () => {
      const [count, setCount] = useState(0);
        // 比較好的作法
      const increment = useCallback(() => {
            // 透過callback拿取前值來修改
        setCount((prevCount) => prevCount + 1);
      }, []);
    
      return (
        <div>
          <ChildComponent onClick={increment} />
          <p>Count: {count}</p>
        </div>
      );
    };
    
    const ChildComponent = ({ onClick }) => {
      console.log('我被渲染啦');
      return <button onClick={onClick}>Increment</button>;
    };
    
    export default ParentComponent;
    

所以在 useCallback 的使用上是要考量你的 function 需不需要大量受到其他參數而重建,如果需要那你乾脆直接往下傳即可,如果不需要你可以考慮擺空陣列 [],或是比較不常被更動的參數為主。

總結

在使用上簡單概括:

  • 對象是參數型別的— useMemo
  • 對象是函數(function)的— useCallback

也不需要過度使用,每次使用就是在進一次深度的比較機制,執行的話也比較耗效能,但好處是能避免不需要重複的情況。

給全新手的大禮包

React基本Hook教學


上一篇
React 走出新手村-Memo處方簽
下一篇
React 走出新手村-深入 Context Provider
系列文
React 走出新手村 31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言